﻿using Dapper;
using Microsoft.AspNetCore.Builder;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Configuration;
using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Reflection;

namespace OSMonitor.App.Core
{
    public static class QuartzExtension
    {
        private static StdSchedulerFactory schedulerFactory;
        private static IScheduler scheduler;
        private static JobKey startJobKey;
        private static TriggerKey startJobTriggerKey;
        private static string connStr;
        public static DateTime? ExecutionTime { get; set; }
        public static bool HasSet { get; set; } = false;
        public static bool Executed { get; set; } = false;
        public static void UseQuartz(this IApplicationBuilder app, IConfiguration configuration)
        {
            connStr =$"Data Source={Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)}/Data/OSMonitor.sqlite" ;
            Executed = GetExecuted();
            //CreateQuartzNetTables();
            ExecutionTime = GetDate();

            var properties = new NameValueCollection
            {
                //// json serialization is the one supported under .NET Core (binary isn't)
                //["quartz.serializer.type"] = "json",

                ["quartz.scheduler.instanceName"] = "DotnetCoreScheduler",
                ["quartz.scheduler.instanceId"] = "instance_one",
                ["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz",
                ["quartz.threadPool.threadCount"] = "5",
                ["quartz.jobStore.misfireThreshold"] = "60000",
                ["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",
                //["quartz.jobStore.type"] = "Quartz.Simpl.RAMJobStore, Quartz",
                ["quartz.jobStore.useProperties"] = "true",
                ["quartz.jobStore.dataSource"] = "default",
                ["quartz.jobStore.tablePrefix"] = "QRTZ_",
                ////["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz",
                ////["quartz.dataSource.default.provider"] = "SqlServer-20",
                ["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SQLiteDelegate, Quartz",
                ["quartz.dataSource.default.provider"] = "SQLite-Microsoft",
                ["quartz.dataSource.default.connectionString"] = connStr,
                ["quartz.serializer.type"] = "binary"
            };

            schedulerFactory = new StdSchedulerFactory(properties);
            scheduler = schedulerFactory.GetScheduler().Result;
            scheduler.Start().Wait();

            startJobKey = new JobKey("StartService");
            startJobTriggerKey = new TriggerKey("StartServiceCron");

            // Get/Create start service job
            var callServiceJob = scheduler.CheckExists(startJobKey).Result ?
                scheduler.GetJobDetail(startJobKey).Result :
                JobBuilder.Create<TimeJob>()
                    .WithIdentity(startJobKey)
                    .Build();

            if (!ExecutionTime.HasValue)
            {
                return;
            }
            if (GetSetted())
                Scheduler();
        }
        public static bool Scheduler()
        {
            scheduler.UnscheduleJob(startJobTriggerKey).Wait();
            // Get/Create start service job
            var callServiceJob = scheduler.CheckExists(startJobKey).Result ?
                scheduler.GetJobDetail(startJobKey).Result :
                JobBuilder.Create<TimeJob>()
                    .WithIdentity(startJobKey)
                    .Build();

            var scheduleTime = ExecutionTime.Value;
            var cronStr = $"{scheduleTime.Second} {scheduleTime.Minute} {scheduleTime.Hour} {scheduleTime.Day} {scheduleTime.Month} ? { scheduleTime.Year}";
            //var cronStrtemp = $"0 0/3 * * * ?";

            var triggerBuilder =
                TriggerBuilder.Create()
                .WithIdentity("StartServiceCron");

            Console.WriteLine("服务启动"+DateTime.Now.ToString("HH:mm:ss"));
            if (Executed)
            {
                triggerBuilder.StartAt(new DateTimeOffset(DateTime.Now.AddMinutes(3)));
            }
            else
            {
                triggerBuilder.WithCronSchedule(cronStr);
            }
            var callServiceTrigger = triggerBuilder.Build();
            try
            {
                scheduler.ScheduleJob(callServiceJob, callServiceTrigger).Wait();
                HasSet = true;
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }

        public static void UnScheduler()
        {
            scheduler.UnscheduleJob(startJobTriggerKey).Wait();
            HasSet = false;
            Executed = false;
            ClearExecuted();
            ClearSet();
        }

        private static void CreateQuartzNetTables()
        {
            using (SqliteConnection connection = new SqliteConnection(connStr))
            {
                connection.Open();
                using (SqliteCommand command = connection.CreateCommand())
                {
                    command.CommandType = System.Data.CommandType.Text;
                    command.CommandText = File.ReadAllText("tables_sqlite.sql");
                    command.ExecuteNonQuery();
                }
                connection.Close();
            }
        }
        private static DateTime GetDate()
        {
            using (SqliteConnection connection = new SqliteConnection(connStr))
            {
                connection.Open();
                var trigger = connection.Query("select * from QRTZ_CRON_TRIGGERS").FirstOrDefault();
                var date = DateTime.Now.AddMinutes(3);
                if (trigger != null)
                {
                    string datestr = trigger.CRON_EXPRESSION;
                    var dateParts = datestr.Split(' ');
                    if (dateParts.Length == 7)
                    {
                        date = new DateTime(
                           Convert.ToInt32(dateParts[6]),
                           Convert.ToInt32(dateParts[4]),
                           Convert.ToInt32(dateParts[3]),
                           Convert.ToInt32(dateParts[2]),
                           Convert.ToInt32(dateParts[1]),
                           Convert.ToInt32(dateParts[0])
                           );
                    }
                }
                connection.Close();
                return date;
            }
        }
        public static void SetExecuted()
        {
            using (SqliteConnection connection = new SqliteConnection(connStr))
            {
                connection.Open();
                connection.Execute("INSERT INTO QRTZ_EXECUTED (SCHED_EXECUTED) VALUES('EXECUTED')");
                connection.Close();
            }
        }

        public static void Set()
        {
            using (SqliteConnection connection = new SqliteConnection(connStr))
            {
                connection.Open();
                connection.Execute("INSERT INTO QRTZ_SET (SCHED_SET) VALUES('EXECUTED')");
                connection.Close();
            }
        }

        public static void ClearSet()
        {
            using (SqliteConnection connection = new SqliteConnection(connStr))
            {
                connection.Open();
                connection.Execute("DELETE FROM QRTZ_SET");
                connection.Close();
            }
        }
        private static bool GetSetted()
        {
            using (SqliteConnection connection = new SqliteConnection(connStr))
            {
                connection.Open();
                var executed = connection.Query("SELECT * FROM QRTZ_SET").Count();
                connection.Close();
                return executed > 0;
            }
        }
        private static void ClearExecuted()
        {
            using (SqliteConnection connection = new SqliteConnection(connStr))
            {
                connection.Open();
                connection.Execute("DELETE FROM QRTZ_EXECUTED");
                connection.Close();
            }
        }
        private static bool GetExecuted()
        {
            using (SqliteConnection connection = new SqliteConnection(connStr))
            {
                connection.Open();
                var executed = connection.Query("SELECT * FROM QRTZ_EXECUTED").Count();
                connection.Close();
                return executed > 0;
            }
        }
    }
}
